﻿#if true
using System;
using Engine;
using Engine.Attributes;

namespace Engine.Components.CompArch
{
	/// <summary>
	/// Enumeration of possible alignment boundaries:
	/// <list type="unordered">
	/// <item>ByteAddressable (1-byte boundaries)</item>
	/// <item>HalfWordAddressable (2-byte boundaries)</item>
	/// <item>WordAddressable (4-byte boundaries)</item>
	/// </list>
	/// </summary>
	public enum AlignmentBoundary
	{
		ByteAddressable = 1,			// 1-byte
		HalfWordAddressable = 2,		// 2-bytes
		WordAddressable = 4,			// 4-bytes
	};

	/// <summary>
	/// Defines useful constants for creating a default Memory module;
	/// </summary>
	public static class MemoryDefaults
	{
		public static readonly UInt32 DEFAULT_SIZE = 256; // in bytes
		public static readonly AlignmentBoundary DEFAULT_ALIGNMENT = AlignmentBoundary.WordAddressable;
	}

	/// <summary>
	/// Defines useful constants for creating a default Cache module
	/// </summary>
	public static class CacheDefaults
	{
		/*
		 * Default cache configuration is an associative-mapped cache with a block (cache-line) size of 8
		 * and an associativity of 8.
		 */
		public static readonly UInt32 DEFAULT_NUM_SET_BITS = 0;
		public static readonly UInt32 DEFAULT_NUM_OFFSET_BITS = 3;
		public static readonly UInt32 DEFAULT_TAG_BITS = 29;
        public static readonly UInt32 DEFAULT_SET_SIZE = 8; // in cache lines
	}

	/// <summary>
	/// Base Component of everything that looks or acts like memory, i.e., caches or honest-to-goodness 
	/// memory banks.
	/// </summary>
	public abstract class MemoryBase : UserCodedComponent
	{
		/// <summary>
		/// Provides the address of the read or write
		/// </summary>
		public readonly InPort<UInt32> Address = new InPort<UInt32>();

        /// <summary>
        /// Tells the memory how much we're going to read or write this time around
        /// </summary>
        public readonly InPort<AlignmentBoundary> Alignment = new InPort<AlignmentBoundary>();

		/// <summary>
		/// Receives the data written to the memory
		/// </summary>
		public readonly InPort<byte[]> WritePort = new InPort<byte[]>();

		/// <summary>
		/// Sends the data read from the memory
		/// </summary>
		public readonly OutPort<byte[]> ReadPort = new OutPort<byte[]>();

		/// <summary>
		/// Set to true if this is a memory write, false if it is a memory read
		/// </summary>
		public readonly InPort<bool> IsWrite = new InPort<bool>();

		/// <summary>
		/// This is true if an invalid address was sent to this memory
		/// </summary>
		public readonly OutPort<bool> InvalidAddressFlag = new OutPort<bool>();

		/// <summary>
		/// The system-wide size, in bytes, of the memory subsystem.
		/// </summary>
		[Config]
		public virtual UInt32 Size { get; set; }


		/// <summary>
		/// True if x is a power of 2, false otherwise
		/// </summary>
		/// <param name="x">the number to check</param>
		/// <returns>see summary</returns>
		protected bool IsPowerOfTwo(UInt32 x)
		{
			if (x == 1 || x == 0) return true;
			if (x % 2 != 0) return false;
			return IsPowerOfTwo(x / 2);
		}

        protected UInt32 ToPowerOf2(UInt32 exp)
        {
            UInt32 result = 1;
            for (UInt32 i = exp; i > 0; i--)
            {
                result *= 2;
            }
            return result;
        }
    }
}
#endif